package server.parser;

import java.util.HashMap;

import server.ConnectionHandler;
import server.datamodel.Cell;
import server.datamodel.CreepDescription;
import server.datamodel.CreepSmashGame;
import server.datamodel.CreepTypeToCreepMapping;
import server.datamodel.Guide;
import server.datamodel.Tile;
import server.datamodel.User;
import server.datamodel.UserAssets;
import server.datamodel.Waypoint;

public class GameParser implements Parser {

	private ConnectionHandler connectionHandler;
	
	public GameParser(ConnectionHandler connectionHandler, CreepSmashGame creepSmashGame) {
		this.connectionHandler = connectionHandler;
		
		init(creepSmashGame);
	}
	
	public void init(CreepSmashGame game) {
		game.addUserInThisGame(connectionHandler);
		
		connectionHandler.sendMessage("SENDING 1000 EVENTS");
		//generate guide
		Guide guide = new Guide();
		guide.setGame(game);
		
		//send game
		sendMessage(game, "guide", null, guide);
		sendMessage(game, "name", null, game.getName());
		sendMessage(game, "numPlayers", null, game.getNumPlayers() + "");
		sendMessage(game, "running", null, game.isRunning() + "");
		sendMessage(game, "testGame", null, game.isTestGame() + "");
		
		//generate user asset for the current user and adds it to the game
		UserAssets userAssets = new UserAssets();
		userAssets.setGame(game);
		userAssets.setUser(connectionHandler.getCurrentUser());
		
		//send user assets
		for(UserAssets u: game.getUsers().values()) {
			sendMessage(game, "users", null, u);
			sendMessage(u, "game", null, game);
			
			sendMessage(u, "user", null, u.getUser());
			sendMessage(u.getUser(), "userAsset", null, u);
			
			sendMessage(u.getUser(), "nickname", null, u.getUser().getNickname());
			sendMessage(u.getUser(), "team", null, u.getUser().getTeam());
			
			//send team info
			sendMessage(u.getUser().getTeam(), "users", null, u.getUser());
			sendMessage(u.getUser().getTeam(), "name", null, u.getUser().getTeam().getName());
		}
		
		//send creep types for creep descriptions
		CreepTypeToCreepMapping creepTypeToCreepMapping = new CreepTypeToCreepMapping();
		sendMessage(guide, "creepDescriptions", null, creepTypeToCreepMapping);
		
		creepTypeToCreepMapping.setGuide(guide);
		creepTypeToCreepMapping.setName("This is a creep type to creep mapping");
		sendMessage(creepTypeToCreepMapping, "key", null, creepTypeToCreepMapping.getName());
		guide.addCreepTypeToCreepMapping(creepTypeToCreepMapping);
		
		//send all creep descriptions
		for(CreepDescription c: connectionHandler.getDatamodel().getCreepDescriptions()) {
			creepTypeToCreepMapping.addCreepDescription(c);
			sendMessage(creepTypeToCreepMapping, "value", null, c);
			sendMessage(c, "name", null, c.getName());
			sendMessage(c, "price", null, c.getPrice() + "");
			sendMessage(c, "healthMax", null, c.getHealthMax() + "");
			sendMessage(c, "bounty", null, c.getBounty() + "");
			sendMessage(c, "income", null, c.getIncome() + "");
			sendMessage(c, "speedMax", null, c.getSpeedMax() + "");
		}
		
		//TODO send all tower descriptions
		
		//send game (starting from creepsmash game)
		for(Tile t: game.getTiles().values()) {
			sendMessage(game, "tiles", null, t);
			sendMessage(t, "game", null, game);
			sendMessage(t, "map", null, t.getMap());
			sendMessage(t, "position", null, t.getPosition());
			if(t.getUserAssets() != null) {
				sendMessage(t, "userAssets", null, t.getUserAssets());
			}
			
			sendMessage(t.getMap(), "background", null, t.getMap().getBackground());
			sendMessage(t.getMap(), "name", null, t.getMap().getName());
			sendMessage(t.getMap(), "tile", null, t);
			sendMessage(t.getMap(), "createdBy", null, t.getMap().getCreatedBy());
			sendMessage(t.getMap(), "version", null, t.getMap().getVersion() + "");
			
			//send all cells of the map
			for(Cell c: t.getMap().getCells().values()) {
				sendMessage(t.getMap(), "cells", null, c);
				
				sendMessage(c, "map", null, t.getMap());
				sendMessage(c, "type", null, c.getType());
				sendMessage(c, "x", null, c.getX() + "");
				sendMessage(c, "y", null, c.getY() + "");
			}
			
			//send all the waypoints of the map
			Waypoint w = t.getMap().getEnterWaypoint();
			sendMessage(t.getMap(), "enterWaypoint", null, t.getMap().getEnterWaypoint());
			while(w != null) {
				sendMessage(t.getMap(), "waypoints", null, w);
				
				sendMessage(w, "cell", null, w.getCell());
				sendMessage(w, "next", null, w.getNext());
				sendMessage(w, "previous", null, w.getPrevious());
				
				w = w.getNext();
			}
			sendMessage(t.getMap(), "exitWaypoint", null, t.getMap().getExitWaypoint());
		}
		
		//send speed at last, because kassem thought, that is always the latest message after loading
		sendMessage(game, "speed", null, game.getSpeed() + "");
	}
	
	private void sendMessage(Object src, String property, Object oldValue, Object newValue) {
		if(oldValue == null && newValue == null) {
			return;
		}
		
		StringBuffer buffer = new StringBuffer();
		buffer.append("{\"@ts\":\"" + System.currentTimeMillis() + "\"");
		buffer.append(",\"@src\":\"" + src.toString() + "\"");
		buffer.append(",\"@prop\":\"" + property + "\"");
		
		if(oldValue != null) {
			buffer.append(",\"@ov\":\"" + oldValue.toString() + "\"");
		}
		
		if(newValue != null) {
			buffer.append(",\"@nv\":\"" + newValue.toString() + "\"");
		}
		
		buffer.append("}");
		
		connectionHandler.sendMessage(buffer.toString());
	}
	
	private void sendMessage(Object src, String property, String oldValue, String newValue) {
		if(oldValue == null && newValue == null) {
			return;
		}
		
		StringBuffer buffer = new StringBuffer();
		buffer.append("{\"@ts\":\"" + System.currentTimeMillis() + "\"");
		buffer.append(",\"@src\":\"" + src.toString() + "\"");
		buffer.append(",\"@prop\":\"" + property + "\"");
		
		if(oldValue != null) {
			buffer.append(",\"@ov\":\"" + oldValue + "\"");
		}
		
		if(newValue != null) {
			buffer.append(",\"@nv\":\"" + newValue + "\"");
		}
		
		buffer.append("}");
		
		connectionHandler.sendMessage(buffer.toString());
	}
	
	@Override
	public void parseMessage(String message) {
		System.out.println("received: " + message);
		
		if(message.startsWith("{")) {
			//parsing incoming message
			String[] split = message.substring(1, message.length() - 1).split("[:,]");
			
			if(split[0].toLowerCase().equals("\"@action\"")) {
				String action = split[1].substring(1, split[1].length() - 1).toLowerCase();
				
				if(action.equals("noop")) {
					System.out.println("received noop");
				} else if(action.equals("start_game")) {
					
				} else if(action.equals("leave_game")) {
					leaveGame();
				} else if(action.equals("message")) {
					
				} else if(action.equals("change_user_color")) {
					
				} else if(action.equals("choose_tile")) {
					chooseTile(getProperties(message));
				} else if(action.equals("create_tower")) {
					
				} else if(action.equals("sell_tower")) {
					
				} else if(action.equals("upgrade_tower")) {
					
				} else if(action.equals("change_tower_strategy")) {
					
				} else if(action.equals("create_creep")) {
					
				}
			} else {
				//unknown command
			}
		}
	}
	
	private HashMap<String, String> getProperties(String message) {
		HashMap<String, String> hashMap = new HashMap<String, String>();
		String pattern = "\"properties\":";
		
		int startIndex = message.indexOf(pattern);
		int endIndex = message.indexOf("}");
		
		String entries = message.substring(startIndex + pattern.length() + 1, endIndex + 1);
		String[] split = entries.split("\"entry\":");
		
		for(int i = 0; i < split.length; i++) {
			if(split[i] == null || split[i].trim().length() == 0) {
				continue;
			}
			
			String replaced = split[i].substring(1, split[i].length() - 1).replaceAll("\"", "");
			String[] split2 = replaced.split(",");
			
			hashMap.put(split2[0].split(":")[1], split2[1].split(":")[1]);
		}
		
		return hashMap;
	}
	
	private void leaveGame() {
		//remove handler from game and remove user assets
		for(CreepSmashGame g: connectionHandler.getDatamodel().getGames()) {
			g.removeUsersInThisGame(connectionHandler);
		}
		
		User currentUser = connectionHandler.getCurrentUser();
		if(currentUser != null) {
			if(currentUser.getUserAssets() != null) {
				if(currentUser.getUserAssets().getTile() != null) {
					currentUser.getUserAssets().setTile(null);
				}
				
				currentUser.getUserAssets().getGame().removeUserAssets(currentUser.getUserAssets());
				currentUser.setUserAssets(null); 
			}
		}
		
		connectionHandler.setCurrentParser(new LoginParser(connectionHandler, true));
	}
	
	private void chooseTile(HashMap<String, String> properties) {
		String tileID = properties.values().iterator().next();
		Tile tile = connectionHandler.getCurrentUser().getUserAssets().getGame().getTile(tileID);
		
		tile.setUserAssets(connectionHandler.getCurrentUser().getUserAssets());
	}
}